package com.cryptoneedle.center.source.service.constraint;


import com.baomidou.dynamic.datasource.annotation.DS;
import com.bubbles.engine.data.core.repository.BaseRepository;
import com.bubbles.engine.data.core.service.BaseService;
import com.cryptoneedle.center.source.entity.constraint.SourceConstraintUnique;
import com.cryptoneedle.center.source.entity.metadata.*;
import com.cryptoneedle.center.source.key.constraint.SourceConstraintColumnKey;
import com.cryptoneedle.center.source.properties.SshProperties;
import com.cryptoneedle.center.source.repository.constraint.SourceConstraintUniqueRepository;
import com.cryptoneedle.center.source.service.enhance.SourceEnhanceDimensionService;
import com.cryptoneedle.center.source.service.enhance.SourceEnhanceDorisAnalyzeColumnService;
import com.cryptoneedle.center.source.service.enhance.SourceEnhanceDorisAnalyzeTableService;
import com.cryptoneedle.center.source.service.enhance.SourceEnhanceIncrementService;
import com.cryptoneedle.center.source.service.metadata.*;
import com.cryptoneedle.center.source.util.JdbcUtil;
import com.cryptoneedle.center.source.util.SshManager;
import com.google.common.collect.Maps;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>description: 源数据源-唯一约束 </p>
 *
 * @author CryptoNeedle
 * @date 2025-06-20
 */
@DS("pg")
@Slf4j
@Service
public class SourceConstraintUniqueService extends BaseService<SourceConstraintUnique, SourceConstraintColumnKey> {
    
    @Resource
    private SourceConstraintUniqueRepository sourceConstraintUniqueRepository;
    
    @Override
    public BaseRepository<SourceConstraintUnique, SourceConstraintColumnKey> getRepository() {
        return this.sourceConstraintUniqueRepository;
    }
    
    @Resource
    private SourceJdbcService sourceJdbcService;
    
    @Resource
    private SourceServerService sourceServerService;
    
    @Resource
    private SourceCatalogService sourceCatalogService;
    
    @Resource
    private SourceDatabaseService sourceDatabaseService;
    
    @Resource
    private SourceTableService sourceTableService;
    
    @Resource
    private SourceColumnService sourceColumnService;
    
    @Resource
    private SourceConstraintPrimaryService sourceConstraintPrimaryService;
    
    @Resource
    private SourceConstraintUniqueService sourceConstraintUniqueService;
    
    @Resource
    private SourceConstraintForeignService sourceConstraintForeignService;
    
    @Resource
    private SourceConstraintCheckService sourceConstraintCheckService;
    
    @Resource
    private SourceIndexUniqueService sourceIndexUniqueService;
    
    @Resource
    private SourceEnhanceDorisAnalyzeTableService sourceEnhanceDorisAnalyzeTableService;
    
    @Resource
    private SourceEnhanceDorisAnalyzeColumnService sourceEnhanceDorisAnalyzeColumnService;
    
    @Resource
    private SourceEnhanceIncrementService sourceEnhanceIncrementService;
    
    @Resource
    private SourceEnhanceDimensionService sourceEnhanceDimensionService;
    
    @Resource
    private SshProperties sshProperties;
    
    @Resource
    private SshManager sshManager;
    
    @Transactional
    public List<SourceConstraintUnique> listByCatalog(SourceCatalog sourceCatalog) {
        return sourceConstraintUniqueRepository.listByCatalog(sourceCatalog.getSourceCatalogName());
    }
    
    @Transactional
    public List<SourceConstraintUnique> listByDatabase(SourceDatabase sourceDatabase) {
        return sourceConstraintUniqueRepository.listByDatabase(sourceDatabase.getSourceCatalogName(), sourceDatabase.getSourceDatabaseName());
    }
    
    @Transactional
    public List<SourceConstraintUnique> listByTable(SourceTable sourceTable) {
        return sourceConstraintUniqueRepository.listByTable(sourceTable.getSourceCatalogName(), sourceTable.getSourceDatabaseName(), sourceTable.getSourceTableName());
    }
    
    @Transactional
    public void addList(List<SourceConstraintUnique> sourceConstraintUniques) {
        sourceConstraintUniqueService.saveAll(sourceConstraintUniques);
    }
    
    @Transactional
    public void saveList(List<SourceConstraintUnique> sourceConstraintUniques) {
        sourceConstraintUniqueService.saveAll(sourceConstraintUniques);
    }
    
    @Transactional
    public void deleteList(List<SourceConstraintUnique> sourceConstraintUniques) {
        sourceConstraintUniqueService.deleteAll(sourceConstraintUniques);
    }
    
    public void syncSource(SourceCatalog sourceCatalog, SourceDatabase sourceDatabase) {
        String sourceCatalogName = sourceCatalog.getSourceCatalogName();
        String sourceDatabaseName = sourceDatabase.getSourceDatabaseName();
        SourceJdbc sourceJdbc = sourceCatalog.getSourceJdbc();
        Map<String, SourceTable> sourceTableMap = Maps.uniqueIndex(sourceTableService.listByDatabase(sourceDatabase), SourceTable::getSourceTableName);
        Map<String, SourceColumn> sourceColumnMap = Maps.uniqueIndex(sourceColumnService.listByDatabase(sourceDatabase), SourceColumn::getStringKey);
        
        JdbcTemplate jdbcTemplate = JdbcUtil.getJdbcTemplate(sourceJdbc);
        List<SourceConstraintUnique> sourceColumns = jdbcTemplate.query(sourceJdbc
                                                                                .getDatabaseType()
                                                                                .getDatabase()
                                                                                .getConstraintUniqueSql(sourceDatabaseName), (resultSet, rowNum) -> {
            SourceConstraintUnique sourceConstraintUnique = new SourceConstraintUnique()
                    .setSourceCatalogName(sourceCatalogName)
                    .setSourceDatabaseName(sourceDatabaseName)
                    .setSourceTableName(resultSet.getString("sourceTableName"))
                    .setSourceConstraintName(resultSet.getString("sourceConstraintName"))
                    .setSourceColumnName(resultSet.getString("sourceColumnName"))
                    .setPosition(resultSet.getInt("position"))
                    .setSourceColumnEnabled(true);
            
            sourceConstraintUnique.setSourceTable(sourceTableMap.get(sourceConstraintUnique.getSourceTableName()));
            sourceConstraintUnique.setSourceColumn(sourceColumnMap.get(sourceConstraintUnique.getColumnStringKey()));
            return sourceConstraintUnique;
        });
        
        sourceConstraintUniqueService.compareSource(sourceDatabase, sourceColumns);
    }
    
    @Transactional
    public void compareSource(SourceDatabase sourceDatabase, List<SourceConstraintUnique> dealList) {
        List<SourceConstraintUnique> originList = sourceConstraintUniqueService.listByDatabase(sourceDatabase);
        // 已存在
        Map<String, SourceConstraintUnique> originMap = Maps.uniqueIndex(originList, origin -> origin.getStringKey());
        // 待处理
        Map<String, SourceConstraintUnique> dealMap = Maps.uniqueIndex(dealList, deal -> deal.getStringKey());
        
        // 新增
        List<SourceConstraintUnique> extraList = dealList.stream().filter(deal -> !originMap.containsKey(deal.getStringKey())).toList();
        
        // 保存
        List<SourceConstraintUnique> existsList = originList.stream().filter(origin -> dealMap.containsKey(origin.getStringKey())).peek(origin -> {
            SourceConstraintUnique deal = dealMap.get(origin.getStringKey());
            origin.setPosition(deal.getPosition());
        }).toList();
        
        // 移除
        List<SourceConstraintUnique> missList = originList.stream().filter(item -> !dealMap.containsKey(item.getStringKey())).toList();
        
        sourceConstraintUniqueService.addList(extraList);
        sourceConstraintUniqueService.saveList(existsList);
        sourceConstraintUniqueService.deleteList(missList);
        
        log.info("[SYNC] {} -> {} -> 元数据采集 -> Unique  Constraint => 共计{} | 新增{} | 删除{}", sourceDatabase.getSourceCatalogName(), sourceDatabase.getSourceDatabaseName(), String.format("%5d", dealList.size()), String.format("%5d", extraList.size()), String.format("%5d", missList.size()) + (
                !missList.isEmpty() ? " => " + missList
                        .stream()
                        .map(sourceConstraintUnique -> sourceConstraintUnique.getSourceTableName() + " - " + sourceConstraintUnique.getSourceConstraintName())
                        .collect(Collectors.joining(",")) : ""));
    }
}